﻿#include<tchar.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#define SYS_ALLOC_BUF 1024*128

using namespace std;

typedef struct _FileEvent//文件事件
{
	DWORD dwAction;
	wstring lpFile;
	string sTime;
	TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
} FileEvent, *PFileEvent;


BOOL IsFile(wstring);
wstring FileName(wstring);
VOID CaseProcess(vector<FileEvent> &_vEvent, FileEvent &_feRecord);
VOID EventLog(wstring File1, wstring File2, wstring Action, string sTime);
string TimeStamp(SYSTEMTIME s);
VOID EventLog(wstring File1, wstring File2, string Action, string Time, TCHAR * Computer);
VOID Welcome();

int main()
{
	HANDLE hDir;
	FILE_NOTIFY_INFORMATION *pInfo;
	TCHAR szBuffer[SYS_ALLOC_BUF / 2] = { 0 };
	DWORD dwOffset = 0;
	DWORD BytesReturned;
	vector<FileEvent> vEvent;
	vector<FileEvent> vCase;

	// Get Directory Handle by CreateFile创建句柄
	hDir = CreateFile(_T("D:\\test"),GENERIC_READ | FILE_LIST_DIRECTORY,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,NULL);

	if (hDir == INVALID_HANDLE_VALUE)//检查句柄创建是否成功
	{
		MessageBox(NULL, _T("CreateFile Fail!"), _T("Fail"), MB_OK);
		return -1;
	}

	// Retreves info that describes the changes within the specified directory 


	cout << "Monitor Begin" << endl;
	//    MessageBox( NULL, _T("Monitor Begin!"), _T("Start"), MB_OK);

	int i;

	while (ReadDirectoryChangesW(hDir,szBuffer,SYS_ALLOC_BUF,TRUE,FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME,&BytesReturned,NULL,NULL))
	{
		cout << endl;
		dwOffset = 0;
		i = 1;
		do
		{
			// Get a pointer to the first change record...
			pInfo = (FILE_NOTIFY_INFORMATION *)&szBuffer[dwOffset / 2];
			dwOffset += pInfo->NextEntryOffset;
			i++;
			// Event Process

			TCHAR FilePath[100] = { 0 };
			memcpy((void *)FilePath, (void *)(pInfo->FileName), pInfo->FileNameLength);
			wstring File(FilePath);
			FileEvent feRecord;
			feRecord.dwAction = pInfo->Action;
			feRecord.lpFile = File;
			// Get SYSTEM time
			SYSTEMTIME s;
			GetLocalTime(&s);
			feRecord.sTime = TimeStamp(s);
			// Get Computer name
			DWORD dwRet = MAX_COMPUTERNAME_LENGTH + 1;
			GetComputerName(feRecord.ComputerName, &dwRet);

			CaseProcess(vEvent, feRecord);




		} while (pInfo->NextEntryOffset != 0);
		// sys-alloc buffer overflowed 
		if (BytesReturned == 0)
			printf("*********************sys-alloc buffer OVERFLOW!************************\n");
		//        printf("Byte Returned : %d\n",BytesReturned);
		memset(szBuffer, 0, sizeof(szBuffer));
	}

	CloseHandle(hDir);
	cout << GetLastError() << endl;
	return 0;
}




BOOL IsFile(wstring sFile)
{
	wstring::size_type last_pos;
	wstring::size_type pos;
	last_pos = sFile.find_last_of(L"\\");
	if (last_pos == string::npos)
		// In the root DIR
	{
		pos = sFile.find(L".");
		if (pos == string::npos)
			return FALSE;
		else
			return TRUE;

	}
	else
		// In Sub Dir
	{
		pos = sFile.find(L".", last_pos);
		if (pos == string::npos)
			return FALSE;
		else
			return TRUE;
	}
}

wstring FileName(wstring sFile)
{
	//    wstring Fname;
	wstring::size_type last_pos;
	last_pos = sFile.find_last_of(L"\\");
	if (last_pos == wstring::npos)// sFile is the filename already
		return sFile;
	else// Get rid of path for filename 
	{
		sFile.erase(0,last_pos + 1);
	return sFile;

	}
}




VOID CaseProcess(vector<FileEvent> &_vEvent, FileEvent &_feRecord)
{
	if (_vEvent.empty())
		// vEvent is empty
	{
		if (IsFile(_feRecord.lpFile))
			// this record is a file 
		{
			_vEvent.push_back(_feRecord);
			return;
		}
		else
			// this record is s dir 
			return;
	}
	else
		// vEvent is NOT empty
	{
		switch (_vEvent[0].dwAction)
		{
			// this is a file ADD event 
		case FILE_ACTION_ADDED:
			if (_vEvent.size() == 1)
				// the vector has only one element now
			{
				if (!IsFile(_feRecord.lpFile))
					// this record is a DIR 
				{
					EventLog(_vEvent[0].lpFile, L"", "ADDED", _feRecord.sTime, _feRecord.ComputerName);
					_vEvent.push_back(_feRecord);
					return;
				}
				else if (_vEvent[0].lpFile == _feRecord.lpFile && _feRecord.dwAction == FILE_ACTION_MODIFIED)
					// this record is a file 
				{
					return;
				}
				else
					// this record is a file , but NOT the same file
				{
					EventLog(_vEvent[0].lpFile, L"", "ADDED", _feRecord.sTime, _feRecord.ComputerName);
					_vEvent.clear();
					if (IsFile(_feRecord.lpFile))
					{
						_vEvent.push_back(_feRecord);
					}
					return;
				}
			}
			else
				// the vector has two element now, the second one is DIR
			{
				if (_vEvent[0].lpFile == _feRecord.lpFile && _feRecord.dwAction == FILE_ACTION_MODIFIED)
					// this record is in the same event
				{
					return;
				}
				else
					// new event , record old event
				{
					_vEvent.clear();
					if (IsFile(_feRecord.lpFile))
					{
						_vEvent.push_back(_feRecord);
					}
					return;
				}
			}

			// this is a file DELETE event
		case FILE_ACTION_REMOVED:
			if (FileName(_vEvent[0].lpFile) == FileName(_feRecord.lpFile) && _feRecord.dwAction == FILE_ACTION_ADDED)
				// new event , record old event
				/*this is a MOVE(cut) event*/
			{
				EventLog(_vEvent[0].lpFile, _feRecord.lpFile, "MOVED", _feRecord.sTime, _feRecord.ComputerName);
				_vEvent.clear();
				return;
			}
			else
				// new event , record old event
				/* this is a DELETE event*/
			{
				if (IsFile(_feRecord.lpFile))
					// this is a file case
				{
					EventLog(_vEvent[0].lpFile, L"", "DELETED", _feRecord.sTime, _feRecord.ComputerName);
					_vEvent.clear();
					_vEvent.push_back(_feRecord);
					return;
				}
				else
					// this is a dir case
				{
					EventLog(_vEvent[0].lpFile, L"", "DELETED", _feRecord.sTime, _feRecord.ComputerName);
					_vEvent.clear();
					return;
				}

			}

			// this is a file MODIFY event
		case FILE_ACTION_MODIFIED:
			if (_vEvent.size() == 1)
				// the vector has only one element now
			{
				if (_vEvent[0].lpFile == _feRecord.lpFile && _feRecord.dwAction == FILE_ACTION_MODIFIED)
					// this is the same event
				{
					EventLog(_vEvent[0].lpFile, L"", "MODIFIED", _feRecord.sTime, _feRecord.ComputerName);
					_vEvent.push_back(_feRecord);
					return;
				}
				else
					// a new event , this is a exception
				{
					EventLog(_vEvent[0].lpFile, L"", " was MODIFIED,But this is a exception(Event Logic Fail).", _feRecord.sTime, _feRecord.ComputerName);
					_vEvent.clear();
					if (IsFile(_feRecord.lpFile))
					{
						_vEvent.push_back(_feRecord);
					}
					return;
				}
			}
			else
				// the vector has 2 element now
			{


				_vEvent.clear();
				if (IsFile(_feRecord.lpFile))
				{
					_vEvent.push_back(_feRecord);
				}
				return;


			}


			// this is a file RENAME event
		case FILE_ACTION_RENAMED_OLD_NAME:
			if (_feRecord.dwAction == FILE_ACTION_RENAMED_NEW_NAME)
				// the new name , same event
			{
				EventLog(_vEvent[0].lpFile, _feRecord.lpFile, "RENAMED", _feRecord.sTime, _feRecord.ComputerName);
				_vEvent.clear();
				return;
			}
			else
				// OLD_NAME is not followed by NEW_NAME , this is a exception
			{
				EventLog(_vEvent[0].lpFile, L"", " was RENAMED,But this is a exception(Event Logic Fail).", _feRecord.sTime, _feRecord.ComputerName);
				_vEvent.clear();
				if (IsFile(_feRecord.lpFile))
				{
					_vEvent.push_back(_feRecord);
				}
				return;
			}

			// sometimes happened eg: test.txt rename to test.cfg under certain circumstance ( we don't monitor txt files)
		case FILE_ACTION_RENAMED_NEW_NAME:
			EventLog(L"\"Some file which we don't monitor\"", _vEvent[0].lpFile, "RENAMED", _feRecord.sTime, _feRecord.ComputerName);
			_vEvent.clear();
			if (IsFile(_feRecord.lpFile))
			{
				_vEvent.push_back(_feRecord);
			}
			return;


			// this should not happen , it is a exception
		default:
			EventLog(L"", L"", "The value of vEvent[0].dwAction should be within 1,2,3,4,5 , This is a exception ", _feRecord.sTime, _feRecord.ComputerName);


		}
	}
}

VOID EventLog(wstring File1, wstring File2, string Action, string Time, TCHAR * Computer)
{

	// change wstring to string
	string file1(File1.begin(), File1.end());
	file1.assign(File1.begin(), File1.end());
	string file2(File2.begin(), File2.end());
	file2.assign(File2.begin(), File2.end());
	wstring Host = Computer;
	string computer(Host.begin(), Host.end());
	computer.assign(Host.begin(), Host.end());


	ofstream hLog;
	//CHAR Record[RECORD] = { 0 };
	string Temp;
	hLog.open("d:\\testlog.csv", ios::app);
	if (!hLog.is_open())
	{
		cout << "未成功打开文件" << endl;
	}
	if (File2 == L"")
		// Only 1 file involved 
	{
		hLog<< "文件" << "," << "行为" << "," << "时间" << "," << "用户" << endl;
		hLog << file1 << "," << Action << "," << Time << "," << computer << endl;

	}
	else
		// 2 files involved
	{

		hLog << "文件1" << "," << "文件2" << ",""行为" << "," << "时间" << "," << "用户" << endl;
		hLog << file1 << "," << file2 << ","<<Action << "," << Time << "," << computer << endl;
	}


	hLog.close();

}

string TimeStamp(SYSTEMTIME s)
{
	string time;
	string month;
	string day;
	string year;
	string hour;
	string min;
	string sec;
	char temp[10] = { 0 };

	itoa(s.wMonth, temp, 10);
	month = temp;
	memset(temp, 0, 10);
	itoa(s.wDay, temp, 10);
	day = temp;
	memset(temp, 0, 10);
	itoa(s.wYear, temp, 10);
	year = temp;
	memset(temp, 0, 10);
	itoa(s.wHour, temp, 10);
	hour = temp;
	memset(temp, 0, 10);
	itoa(s.wMinute, temp, 10);
	min = temp;
	memset(temp, 0, 10);
	itoa(s.wSecond, temp, 10);
	sec = temp;
	memset(temp, 0, 10);
	time = month + "/" + day + "/" + year + " " + hour + ":" + min + ":" + sec;
	return time;

}


